package cn.ljy.authority.config.security;

import cn.ljy.authority.web.controller.backstage.BaseBackstageController;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

/**
 * 通过 Spring Security 现有的扩展点，用更可靠的方式实现了权限控制
 * 相当于使用新方案（方法调用 -> 权限计算 -> 权限验证）替换了原来默认的方案（SpEL 解析 -> 权限字符串拼接 -> 权限验证）
 * 简单的说，就是原方案无法识别@PreAuthorize("hasAuthority(#this.getAuthorityPrefix() + ':query')"),而新方案可以配合本业务识别。
 * 这样就可以用于配合通用控制器的权限检查，从而极大简化子类控制器对接口权限的重复描述和覆盖
 * 加上这个业务后，就可以直接在通用控制器使用注解：@PreAuthorize("@authCheckService.check(this, 'query')")
 */
@Service("authCheckService")
public class AuthCheckService {
    public boolean check(BaseBackstageController<?, ?, ?> controller, String action) {
        try {
            //获取自定义通用控制器的请求，即获取@PreAuthorize中的权限字符串，例如：mes:material:add
            String authority = controller.getRequiredAuthority(action);
            if (authority == null){//如果前缀为null，标签该模块无权限验证。
                return true; // 无权限要求
            }
            //获取当前登录账户的权限信息
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            //检查登录账户是否拥有否包含该访问控制器请求的权限
            return auth.getAuthorities().stream()
                    .anyMatch(g -> g.getAuthority().equals(authority));
        } catch (Exception e) {
            throw new SecurityException("权限检查失败", e);
        }
    }
}
